1
2
3
4
5
6
7
8
9
10
11
12
13 package org.apache.tapestry5.internal.services.ajax;
14
15 import org.apache.tapestry5.Asset;
16 import org.apache.tapestry5.BooleanHook;
17 import org.apache.tapestry5.ComponentResources;
18 import org.apache.tapestry5.FieldFocusPriority;
19 import org.apache.tapestry5.func.F;
20 import org.apache.tapestry5.func.Worker;
21 import org.apache.tapestry5.internal.InternalConstants;
22 import org.apache.tapestry5.internal.services.DocumentLinker;
23 import org.apache.tapestry5.internal.services.javascript.JavaScriptStackPathConstructor;
24 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
25 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
26 import org.apache.tapestry5.ioc.util.IdAllocator;
27 import org.apache.tapestry5.json.JSONArray;
28 import org.apache.tapestry5.json.JSONObject;
29 import org.apache.tapestry5.services.javascript.*;
30
31 import java.util.*;
32
33 public class JavaScriptSupportImpl implements JavaScriptSupport
34 {
35 private final IdAllocator idAllocator;
36
37 private final DocumentLinker linker;
38
39
40
41 private final Map<String, Boolean> addedStacks = CollectionFactory.newCaseInsensitiveMap();
42
43 private final Set<String> otherLibraries = CollectionFactory.newSet();
44
45 private final Set<String> importedStylesheetURLs = CollectionFactory.newSet();
46
47 private final List<StylesheetLink> stylesheetLinks = CollectionFactory.newList();
48
49 private final List<InitializationImpl> inits = CollectionFactory.newList();
50
51 private final JavaScriptStackSource javascriptStackSource;
52
53 private final JavaScriptStackPathConstructor stackPathConstructor;
54
55 private final boolean partialMode;
56
57 private final BooleanHook suppressCoreStylesheetsHook;
58
59 private FieldFocusPriority focusPriority;
60
61 private String focusFieldId;
62
63 private Map<String, String> libraryURLToStackName, moduleNameToStackName;
64
65 class InitializationImpl implements Initialization
66 {
67 InitializationPriority priority = InitializationPriority.NORMAL;
68
69 final String moduleName;
70
71 String functionName;
72
73 JSONArray arguments;
74
75 InitializationImpl(String moduleName)
76 {
77 this.moduleName = moduleName;
78 }
79
80 public Initialization invoke(String functionName)
81 {
82 assert InternalUtils.isNonBlank(functionName);
83
84 this.functionName = functionName;
85
86 return this;
87 }
88
89 public Initialization priority(InitializationPriority priority)
90 {
91 assert priority != null;
92
93 this.priority = priority;
94
95 return this;
96 }
97
98 public void with(Object... arguments)
99 {
100 assert arguments != null;
101
102 this.arguments = new JSONArray(arguments);
103 }
104 }
105
106 public JavaScriptSupportImpl(DocumentLinker linker, JavaScriptStackSource javascriptStackSource,
107 JavaScriptStackPathConstructor stackPathConstructor, BooleanHook suppressCoreStylesheetsHook)
108 {
109 this(linker, javascriptStackSource, stackPathConstructor, new IdAllocator(), false, suppressCoreStylesheetsHook);
110 }
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 public JavaScriptSupportImpl(DocumentLinker linker, JavaScriptStackSource javascriptStackSource,
132 JavaScriptStackPathConstructor stackPathConstructor, IdAllocator idAllocator, boolean partialMode,
133 BooleanHook suppressCoreStylesheetsHook)
134 {
135 this.linker = linker;
136 this.idAllocator = idAllocator;
137 this.javascriptStackSource = javascriptStackSource;
138 this.stackPathConstructor = stackPathConstructor;
139 this.partialMode = partialMode;
140 this.suppressCoreStylesheetsHook = suppressCoreStylesheetsHook;
141
142
143
144
145 if (partialMode)
146 {
147 addedStacks.put(InternalConstants.CORE_STACK_NAME, true);
148 }
149 }
150
151 public void commit()
152 {
153 if (focusFieldId != null)
154 {
155 require("t5/core/pageinit").invoke("focus").with(focusFieldId);
156 }
157
158 F.flow(stylesheetLinks).each(new Worker<StylesheetLink>()
159 {
160 public void work(StylesheetLink value)
161 {
162 linker.addStylesheetLink(value);
163 }
164 });
165
166 F.flow(inits).sort(new Comparator<InitializationImpl>()
167 {
168 public int compare(InitializationImpl o1, InitializationImpl o2)
169 {
170 return o1.priority.compareTo(o2.priority);
171 }
172 }).each(new Worker<InitializationImpl>()
173 {
174 public void work(InitializationImpl element)
175 {
176 linker.addInitialization(element.priority, element.moduleName, element.functionName, element.arguments);
177 }
178 });
179 }
180
181 public void addInitializerCall(InitializationPriority priority, String functionName, JSONObject parameter)
182 {
183 createInitializer(priority).with(functionName, parameter);
184 }
185
186 public void addInitializerCall(String functionName, JSONArray parameter)
187 {
188 addInitializerCall(InitializationPriority.NORMAL, functionName, parameter);
189 }
190
191 public void addInitializerCall(InitializationPriority priority, String functionName,
192 JSONArray parameter)
193 {
194
195
196
197 List parameterList = new ArrayList(parameter.length() + 1);
198 parameterList.add(functionName);
199 parameterList.addAll(parameter.toList());
200 createInitializer(priority).with(parameterList.toArray());
201 }
202
203 private Initialization createInitializer(InitializationPriority priority)
204 {
205 assert priority != null;
206
207 importCoreStack();
208
209 return require("t5/core/init").priority(priority);
210 }
211
212 public void addInitializerCall(String functionName, JSONObject parameter)
213 {
214 addInitializerCall(InitializationPriority.NORMAL, functionName, parameter);
215 }
216
217 public void addInitializerCall(InitializationPriority priority, String functionName, String parameter)
218 {
219 createInitializer(priority).with(functionName, parameter);
220 }
221
222 public void addInitializerCall(String functionName, String parameter)
223 {
224 addInitializerCall(InitializationPriority.NORMAL, functionName, parameter);
225 }
226
227 public void addScript(InitializationPriority priority, String format, Object... arguments)
228 {
229 assert priority != null;
230 assert InternalUtils.isNonBlank(format);
231
232 importCoreStack();
233
234 String newScript = arguments.length == 0 ? format : String.format(format, arguments);
235
236 if (partialMode)
237 {
238 require("t5/core/pageinit").invoke("evalJavaScript").with(newScript);
239 } else
240 {
241 linker.addScript(priority, newScript);
242 }
243 }
244
245 public void addScript(String format, Object... arguments)
246 {
247 addScript(InitializationPriority.NORMAL, format, arguments);
248 }
249
250 public void addModuleConfigurationCallback(ModuleConfigurationCallback callback)
251 {
252 linker.addModuleConfigurationCallback(callback);
253 }
254
255 public String allocateClientId(ComponentResources resources)
256 {
257 return allocateClientId(resources.getId());
258 }
259
260 public String allocateClientId(String id)
261 {
262 return idAllocator.allocateId(id);
263 }
264
265 public JavaScriptSupport importJavaScriptLibrary(Asset asset)
266 {
267 assert asset != null;
268
269 return importJavaScriptLibrary(asset.toClientURL());
270 }
271
272 public JavaScriptSupport importJavaScriptLibrary(String libraryURL)
273 {
274 importCoreStack();
275
276 String stackName = findStackForLibrary(libraryURL);
277
278 if (stackName != null)
279 {
280 return importStack(stackName);
281 }
282
283 if (!otherLibraries.contains(libraryURL))
284 {
285 linker.addLibrary(libraryURL);
286
287 otherLibraries.add(libraryURL);
288 }
289
290 return this;
291 }
292
293 private void importCoreStack()
294 {
295 addAssetsFromStack(InternalConstants.CORE_STACK_NAME);
296 }
297
298
299
300
301
302 private String findStackForLibrary(String libraryURL)
303 {
304 return getLibraryURLToStackName().get(libraryURL);
305 }
306
307
308 private Map<String, String> getLibraryURLToStackName()
309 {
310 if (libraryURLToStackName == null)
311 {
312 libraryURLToStackName = CollectionFactory.newMap();
313
314 for (String stackName : javascriptStackSource.getStackNames())
315 {
316 for (Asset library : javascriptStackSource.getStack(stackName).getJavaScriptLibraries())
317 {
318 libraryURLToStackName.put(library.toClientURL(), stackName);
319 }
320 }
321 }
322
323 return libraryURLToStackName;
324 }
325
326 private String findStackForModule(String moduleName)
327 {
328 return getModuleNameToStackName().get(moduleName);
329 }
330
331 private Map<String, String> getModuleNameToStackName()
332 {
333
334 if (moduleNameToStackName == null)
335 {
336 moduleNameToStackName = CollectionFactory.newMap();
337
338 for (String stackName : javascriptStackSource.getStackNames())
339 {
340 for (String moduleName : javascriptStackSource.getStack(stackName).getModules())
341 {
342 moduleNameToStackName.put(moduleName, stackName);
343 }
344 }
345 }
346
347 return moduleNameToStackName;
348 }
349
350
351 private void addAssetsFromStack(String stackName)
352 {
353 if (addedStacks.containsKey(stackName))
354 {
355 return;
356 }
357
358 JavaScriptStack stack = javascriptStackSource.getStack(stackName);
359
360 for (String dependentStackname : stack.getStacks())
361 {
362 addAssetsFromStack(dependentStackname);
363 }
364
365 addedStacks.put(stackName, true);
366
367 boolean addAsCoreLibrary = stackName.equals(InternalConstants.CORE_STACK_NAME);
368
369 List<String> libraryURLs = stackPathConstructor.constructPathsForJavaScriptStack(stackName);
370
371 for (String libraryURL : libraryURLs)
372 {
373 if (addAsCoreLibrary)
374 {
375 linker.addCoreLibrary(libraryURL);
376 } else
377 {
378 linker.addLibrary(libraryURL);
379 }
380 }
381
382 if (!(addAsCoreLibrary && suppressCoreStylesheetsHook.checkHook()))
383 {
384 stylesheetLinks.addAll(stack.getStylesheets());
385 }
386
387 String initialization = stack.getInitialization();
388
389 if (initialization != null)
390 {
391 addScript(InitializationPriority.IMMEDIATE, initialization);
392 }
393 }
394
395 public JavaScriptSupport importStylesheet(Asset stylesheet)
396 {
397 assert stylesheet != null;
398
399 return importStylesheet(new StylesheetLink(stylesheet));
400 }
401
402 public JavaScriptSupport importStylesheet(StylesheetLink stylesheetLink)
403 {
404 assert stylesheetLink != null;
405
406 importCoreStack();
407
408 String stylesheetURL = stylesheetLink.getURL();
409
410 if (!importedStylesheetURLs.contains(stylesheetURL))
411 {
412 importedStylesheetURLs.add(stylesheetURL);
413
414 stylesheetLinks.add(stylesheetLink);
415 }
416
417 return this;
418 }
419
420 public JavaScriptSupport importStack(String stackName)
421 {
422 assert InternalUtils.isNonBlank(stackName);
423
424 importCoreStack();
425
426 addAssetsFromStack(stackName);
427
428 return this;
429 }
430
431 public JavaScriptSupport autofocus(FieldFocusPriority priority, String fieldId)
432 {
433 assert priority != null;
434 assert InternalUtils.isNonBlank(fieldId);
435
436 if (focusFieldId == null || priority.compareTo(focusPriority) > 0)
437 {
438 this.focusPriority = priority;
439 focusFieldId = fieldId;
440 }
441
442 return this;
443 }
444
445 public Initialization require(String moduleName)
446 {
447 assert InternalUtils.isNonBlank(moduleName);
448
449 importCoreStack();
450
451 String stackName = findStackForModule(moduleName);
452
453 if (stackName != null)
454 {
455 importStack(stackName);
456 }
457
458 InitializationImpl init = new InitializationImpl(moduleName);
459
460 inits.add(init);
461
462 return init;
463 }
464
465 }